package com.momoc.mrpc.protocol;

import com.momoc.mrpc.rpc.message.Message;
import com.momoc.mrpc.rpc.serialize.MRpcSerializeMode;
import com.momoc.mrpc.rpc.serialize.SerializeManager;
import com.momoc.mrpc.util.SequenceIdGenerator;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List;
import lombok.extern.slf4j.Slf4j;

/**
 * @author momoc
 * @version 1.0
 * @className MRpcProtocolCodec
 * @description
 * @date 2022/7/25 2:27 下午
 *
 * 魔数：通信的一个暗号，通常采用固定的几个字节表示。用于防止任何人随便向服务器的端口上发送数据。接收方接收到数据后会先解析出魔书，如果魔书和约定的不匹配，认为是非法数据，直接关闭连接或者采取其他措施以增强系统的安全防护
 * 协议版本号：随着业务需求的变化，协议可能需要对结构或者字段进行改动，不同版本的协议对应的解析方法也不同
 * 序列化算法：表示数据发送方如何将请求对象转化为二进制流，以及如何再将二进制流转为对象
 * 报文类型：不同的业务场景中，报文可能存在不同的类型。如，RPC框架中有请求、响应、心跳等类型的报文；IM即时通信中有登录、创建群聊、发送消息、接收消息、退出群聊等类型的报文
 * 长度域字段：代表请求数据的长度，接收方根据该字段获取完整的报文
 * 请求数据：序列化后的二进制流
 * 状态：表示请求是否正常。一般由调用方设置。如：一次RPC调用失败，状态字段可以被服务提供方设置为异常状态
 * 保留字段：为了应对协议升级的可能性，预留的若干字段
 */
@Slf4j
public class MRpcProtocolCodec extends MessageToMessageCodec<ByteBuf, Message> {

    private static int magicCode = 121416;
    private static byte version = 1;


    //出站
    @Override
    public void encode(ChannelHandlerContext ctx, Message msg, List<Object> out) throws Exception {
        ByteBuf buffer = ctx.alloc().buffer();
        //魔数4个字节
        buffer.writeInt(magicCode);
        //版本号1字节
        buffer.writeByte(version);
        //序列化算法1字节
        buffer.writeByte(SerializeManager.getMRpcSerializeMode().getSerializeTypeByte());
        //消息类型：保留吧，现在只有远程调用 4字节
        buffer.writeInt(msg.getMessageType());
        //4
        buffer.writeInt(msg.getSequenceId());
        byte[] bytes = SerializeManager.getMRpcSerializeMode().toSerialize(msg);
        //长度
        buffer.writeInt(bytes.length);
        //正文内容
        buffer.writeBytes(bytes);
        out.add(buffer);
    }

    //入站
    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magicNum = in.readInt();
        byte version = in.readByte();
        byte serializerAlgorithm = in.readByte(); // 0 或 1
        int messageType = in.readInt(); // 0,1,2...
        in.readInt();
        int length = in.readInt();
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length);
        // 找到反序列化算法
        MRpcSerializeMode mRpcSerializeMode = SerializeManager.getMRpcSerializeMode(serializerAlgorithm);
        // 确定具体消息类型
        Message message = mRpcSerializeMode.toJava(Message.getMessageClass(messageType), bytes);
        out.add(message);
    }

}
